/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "services_fwk.h"

#include <stdbool.h>
#include <stddef.h>

#include "logger.h"

#define MAX_SUB_SERVICE_CNT 4
#define MAX_IPC_INPUT_LEN 2048

typedef struct ServiceDispatch {
    uint32_t sid;
    ServiceProcessCommand *process;
    ServiceCheckPermission *checker;
} ServiceDispatch;

typedef struct SrvDispatchMap {
    struct ServiceDispatch dispatch[MAX_SUB_SERVICE_CNT + 1];
    uint32_t size;
} SrvDispatchMap;

static SrvDispatchMap *GetSrvDispatchMap(void)
{
    static SrvDispatchMap map = {{0}, 0};
    return &map;
}

static bool CheckSharedBuffer(const SharedDataBuffer *buffer)
{
    if (buffer == NULL) {
        LOG_ERROR("buffer is nullptr");
        return false;
    }
    if (buffer->dataSize > buffer->dataMaxSize) {
        LOG_ERROR("buffer is invalid dataSize %u > dataMaxSize %u", buffer->dataSize, buffer->dataMaxSize);
        return false;
    }

    if (buffer->dataMaxSize > MAX_IPC_INPUT_LEN) {
        LOG_ERROR("buffer is invalid dataMaxSize %u", buffer->dataMaxSize);
        return false;
    }
    return true;
}

void ServiceFrameworkAddService(uint32_t sid, ServiceProcessCommand *process, ServiceCheckPermission *checker)
{
    SrvDispatchMap *map = GetSrvDispatchMap();
    if (map == NULL) {
        LOG_ERROR("GetSrvDispatchMap is null");
        return;
    }

    if (map->size >= MAX_SUB_SERVICE_CNT - 1) {
        LOG_ERROR("GetSrvDispatchMap size is invalid");
        return;
    }

    if (process == NULL) {
        LOG_ERROR("input fn is nullptr");
        return;
    }
    map->dispatch[map->size].sid = sid;
    map->dispatch[map->size].process = process;
    map->dispatch[map->size].checker = checker;
    map->size++;
}

ResultCode ServiceInvokeCommand(uint32_t sender, uint32_t sid, uint32_t cmd, SharedDataBuffer *sharedData)
{
    if (!CheckSharedBuffer(sharedData)) {
        LOG_ERROR("CheckSharedBuffer is error");
        return INVALID_PARA_NULL_PTR;
    }

    SrvDispatchMap *map = GetSrvDispatchMap();
    if (map == NULL) {
        LOG_ERROR("GetSrvDispatchMap is null");
        return INVALID_PARA_NULL_PTR;
    }

    ServiceProcessCommand *process = NULL;
    ServiceCheckPermission *checker = NULL;
    for (uint32_t i = 0; i < map->size; i++) {
        if (map->dispatch[i].sid == sid) {
            process = map->dispatch[i].process;
            checker = map->dispatch[i].checker;
            break;
        }
    }

    if (process == NULL) {
        LOG_ERROR("invalid service id = 0x%x", sid);
        return INVALID_PARA_ERR_CMD;
    }

    if (checker == NULL || checker(sender, sid, cmd) != SUCCESS) {
        LOG_ERROR("check perm for sid = 0x%x failed", sid);
        return INVALID_PERM;
    }

    return process(sender, cmd, sharedData);
}
